﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using XLua;

internal struct TimeCall
{
    public long time;
    public Action action;
}
public class GameEnvMrg : Singleton<GameEnvMrg>
{
    internal static float lastGCTime = 0;
    internal const float GCInterval = 1;//1 second 

    private LuaScripts _luaScripts;
    private LuaEnv _luaEnv;
    private bool _isLocal = false;
    public bool isLocal { get { return _isLocal; } }


    Dictionary<string, GameObject> _luaObjList = null;
    void InitializedEnv()
    {
        if(_luaEnv == null)
        {
            _luaScripts = new LuaScripts();
            _luaEnv = new LuaEnv();
            _luaEnv.AddLoader((ref string filename) =>
            {
                filename = filename.ToLower().Replace(".", "/");
                return CustomLoader(filename);
            });
            _luaObjList = new Dictionary<string, GameObject>();
        }
    }
    private byte[] CustomLoader(string filename)
    {
        byte[] bytes = null;
        if (!_isLocal)
        {
            string fn = filename.Substring(filename.LastIndexOf("/") + 1);
            filename= filename.Substring(0, filename.LastIndexOf("/"+ fn));
            AssetBundle scriptAssetBundle = LoadMrg.Instance.LoadAssetBundleScripts(filename);
            TextAsset textAsset = scriptAssetBundle.LoadAsset<TextAsset>(fn);
            bytes = Encoding.UTF8.GetBytes(textAsset.text);
        }
        else
        {
            string luaContent = GetLua(filename, "");
            bytes = Encoding.UTF8.GetBytes(luaContent);
        }
        return bytes;
    }
   
    /// <summary>
    /// 初始化lua
    /// </summary>
    /// <param name="local"></param>
    public void Init(bool local=false,string rootPath= "luascripts")
    {
        InitializedEnv();
        _isLocal = local;
        LoadMrg.Instance.scriptRoot = rootPath;
        _luaScripts.rootPath = rootPath;
        string packageName = "comm";
        string assetName = "App";
        string content = "";
        if (_isLocal)
        {
            Load(Application.dataPath + "/" + rootPath);
            content = GetLua(packageName, assetName);
        }
        else
        {
            AssetBundle assetBundle=LoadMrg.Instance.LoadAssetBundleScripts(packageName);
            TextAsset textAsset = assetBundle.LoadAsset<TextAsset>(assetName);
            content = textAsset.text;
        }
        _luaEnv.DoString(content, packageName, _luaEnv.Global);
        _luaEnv.Global.Get<Action>(assetName+"Init")?.Invoke();
    }
    public void Load(string local) { _luaScripts?.Load(local); }
    public string GetLua(string bundleName, string assetsName) { return _luaScripts?.GetLua(bundleName, assetsName);}
    
    public bool HasLua(string bundleName, string assetsName) { return _luaScripts.HasLua(bundleName, assetsName); }
    public void RemoveLua(string name) { _luaScripts?.RemoveLua(name); }

    
    public T Bind<T>(GameObject go, string bundleName, string assetName,object param=null) where T : LuaBase
    {
        T baselua=go.AddComponent<T>();
        baselua.bundleName=bundleName;
        baselua.assetName=assetName;
        LuaTable meta = _luaEnv.NewTable();
        meta.Set("__index", _luaEnv.Global);
        baselua.table = _luaEnv.NewTable();
        baselua.table.SetMetaTable(meta);
        meta.Dispose();
        baselua.table.Set("this", baselua);
        baselua.table.Set("node", go);
        if (param != null) baselua.table.Set("data",param);
        _luaEnv.DoString(GetLua(bundleName, assetName), assetName, baselua.table);
        Action luaAwake = baselua.table.Get<Action>("awake");
        baselua.table.Get("start", out baselua.luaStart);
        baselua.table.Get("update", out baselua.luaUpdate);
        baselua.table.Get("ondestroy", out baselua.luaOnDestroy);
        baselua.table.Get("onCollisionEnter", out baselua.luaOnCollisionEnter); 
        baselua.table.Get("onCollisionExit", out baselua.luaOnCollisionExit);
        luaAwake?.Invoke();
        return baselua;
    }
    
    public LuaBase BindScrip(GameObject go, string packageName, string scriptName, object param = null)
    {
        packageName= packageName.ToLower();  
        scriptName = scriptName.ToLower();   
        return Bind<LuaBase>(go, packageName, scriptName, param);
    }
    void Update()
    {
        if (Time.time - GameEnvMrg.lastGCTime > GCInterval)
        {
            _luaEnv.Tick();
            GameEnvMrg.lastGCTime = Time.time;
        }
    }

    void Dispose()
    {
        _luaEnv?.Dispose();
        _luaEnv=null;
    }
}